<?php
declare (strict_types = 1);

namespace think\addons;

use think\facade\Config;
use think\facade\Cache;
use think\facade\Event;
use think\Response;
use think\Route;
use think\addons\Middleware AS AddonsMiddleware;

/**
 * 插件服务类
 */
class Service extends \think\Service
{
    private $addons_path;
    private $addons_data;
    private $addons_data_info;

    // 服务注册
    public function register(){
        error_reporting(0);

        $this->app->bind('addons', Service::class);

        require_once dirname(__DIR__). DIRECTORY_SEPARATOR .'common.php';

        $this->autoload();

        // 注册插件事件hook
        $this->loadEvent();

        // 设置插件注解路由配置
        $this->setAnnconf();

        // 加载插件服务
        $this->loadService();
    }

    // 服务启动
    public function boot(){
        $this->registerRoutes(function(Route $route){
            $route->pattern([
                'id'   => '[\d\,]+',
                'ids'   => '[\d\,\w\-]+',
            ]);
            $route->miss(function() {
                $response = Response::create(['code'=>404,'msg'=>'[404] 路由不存在','data'=>[]],'json', 403)->header([]);
                abort($response);
            });

            $this->setRewrite($route);
        });
    }

    /**
     * 插件事件
     */
    private function loadEvent(){
        $hooks = $this->app->isDebug() ? [] : Cache::get('hooks', []);
        if (empty($hooks)) {
            $hooks = Config::get('addons.hooks', []);
            // 初始化钩子
            foreach ($hooks as $key => $values) {
                if (is_string($values)) {
                    $values = explode(',', $values);
                } else {
                    $values = (array)$values;
                }
                $hooks[$key] = array_filter(array_map(function ($v) use ($key) {
                    $addon = '\\addons\\'.$v.'\\'.ucfirst($v);
                    return $addon?[$addon,$key]:[];
                }, $values));
            }
            Cache::set('hooks', $hooks);
        }
        //如果在插件中有定义 appInit,则直接执行
        if (isset($hooks['appInit'])) {
            foreach ($hooks['appInit'] as $k => $v) {
                invoke($v);
            }
        }
        Event::listenEvents($hooks);
    }

    /**
     * 自动载入插件钩子
     * @return bool
     */
    private function autoload(){
        // 是否处理自动载入
        if (!Config::get('addons.autoload', true)) {
            return true;
        }
        $config = Config::get('addons');
        // 读取插件目录及钩子列表
        $base = get_class_methods(\think\Addons::class);
        $addonList = get_addons_list();
        // 遍历插件
        foreach ($addonList as $info) {
            if(!$info['state']) continue;//插件未安装
            if(!class_exists(get_addon_class($info['name']))) continue;//入口类不存在

            $methods = get_class_methods("\\addons\\" . $info['name'] . "\\" . ucfirst($info['name']));
            $this->addons_data[] = $info['name'];
            $this->addons_data_info[$info['name']] = $info;
            // 跟插件基类方法做比对，得到差异结果
            $hooks = array_diff($methods, $base);
            // 循环将钩子方法写入配置中
            foreach ($hooks as $hook) {
                if (!isset($config['hooks'][$hook])) {
                    $config['hooks'][$hook] = [];
                }
                // 兼容手动配置项
                if (is_string($config['hooks'][$hook])) {
                    $config['hooks'][$hook] = explode(',', $config['hooks'][$hook]);
                }
                if (!in_array($info['name'], $config['hooks'][$hook])) {
                    $config['hooks'][$hook][] = $info['name'];
                }
            }
        }
        //插件配置信息保存到缓存
        Cache::set('addons_config',$config);
        //插件列表
        Cache::set('addons_data', $this->addons_data);
        //插件ini列表
        Cache::set('addons_data_info', $this->addons_data_info);
        //插件config列表
        Config::set($config, 'addons');
    }

    /**
     * 插件注解路由配置
     */
    private function setAnnconf(){
        $annconf = Config::get('annotation',[]);
        $annconf['route']['controllers'] = $annconf['route']['controllers']??[];

        $addonList = Cache::get('addons_data_info')?:[];
        $addonList = array_filter($addonList,fn($x)=>$x['state']);
        foreach ($addonList as $addon) {
            $controller_path = addons_path($addon['name'].DS.'controller'.DS.'admin');
            if (is_dir($controller_path)) {
                $annconf['route']['controllers'][$controller_path] = [
                    'name' => 'admin/addons/'.$addon['name'],
                    'append' => [
                        'addons' => $addon['name'],
                    ],
                    'middleware' => [
                        AddonsMiddleware::class,
                    ],
                ];
            }

            $controller_path = addons_path($addon['name'].DS.'controller');
            if (is_dir($controller_path)) {
                $annconf['route']['controllers'][$controller_path] = [
                    'name' => $addon['name'],
                    'append' => [
                        'addons' => $addon['name'],
                    ],
                    'middleware' => [
                        AddonsMiddleware::class,
                    ],
                ];
            }
        }
        Config::set($annconf,'annotation');
    }

    /**
     * 读取插件伪静态
     */
    private function setRewrite(Route $route){
        $controllerSuffix = Config::get('route.controller_suffix') ? 'Controller' : '';
        $addonList = Cache::get('addons_data_info')?:[];
        $addonList = array_filter($addonList,fn($x)=>$x['state']);
        foreach ($addonList as $addon) {
            $conf = get_addon_config($addon['name']);
            if ($conf) {
                $conf['rewrite'] = $conf['rewrite']??[];
                $rule = array_map(fn($value)=>$value,
                            array_flip(array_map(fn($value)=>'admin'.$value,
                                            $conf['rewrite'])));
                $routes = array_map(fn($k,$v)=>[$k,$v],array_keys($rule),array_values($rule));
                foreach ($routes as $r) {
                    $controller = explode('/',strval($r[1]));
                    if(count($controller)<=1)continue;
                    $action = array_pop($controller);
                    $controller[count($controller)-1] = ucfirst($controller[count($controller)-1]);
                    $controller = 'addons\\'.$addon['name'].'\\controller\\'.implode('\\',$controller).$controllerSuffix;
                    $method = '*';
                    if (str_contains($r[0],'#')) {
                        list($r[0],$method) = explode('#',$r[0]);
                    }
                    $route->rule($r[0],$controller.'/'.$action,$method)->middleware(AddonsMiddleware::class)->append(['addons'=>$addon['name']]);
                }
            }
        }
    }

    /**
     * 加载插件服务
     */
    private function loadService(){
        $addons = get_addons_list();
        $bind = [];
        foreach ($addons as $addon) {
            if(!$addon['state']) continue;
            $service_file = addons_path($addon['name']).'service.php';
            if(!is_file($service_file)) continue;
            $services = include($service_file);
            if(!$services) continue;
            foreach ($services as $service) {
                if (class_exists($service)) {
                    $this->app->register($service,$force=true);
                }
                $bind = array_merge($bind,$services);
            }
        }
    }

}